package middleware

import (
	"a-admin/web/resp"
	"a-common/utils/ipaddr"
	"a-system/domain/pojo"
	"encoding/json"
	"log"
	"log/slog"
	"net/url"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"
)

type OperType int

const (
	OTHER   OperType = iota // 其他
	INSERT                  // 新增
	UPDATE                  // 修改
	DELETE                  // 删除
	GRANT                   // 授权
	EXPORT                  // 导出
	IMPORT                  // 导入
	FORCE                   // 强退
	GENCODE                 // 生成代码
	CLEAN                   // 清空数据
)

func (f OperType) String() string {
	return [...]string{"OTHER", "INSERT", "UPDATE", "DELETE", "GRANT", "EXPORT", "IMPORT", "FORCE", "GENCODE", "CLEAN"}[f]
}

// OperLogHandler 操作日志
func OperLogHandler(title string, businessType OperType) fiber.Handler {
	return func(c *fiber.Ctx) error {
		start := time.Now()

		// 拦截请求入参
		param := "-"

		method := c.Method()
		m := fiber.Map{}
		if method == "GET" || method == "DELETE" {
			params := c.Context().QueryArgs()
			params.VisitAll(func(key, value []byte) {
				// 判断参数是否为文件类型
				if strings.HasPrefix(string(value), "@") {
					// 文件类型参数，将其替换为 '-'
					m[string(key)] = "-"
				} else {
					// fmt.Printf("key: %s value: %s\n", key, value)
					m[string(key)] = string(value)
				}
			})
		} else {
			// 解析JSON请求体
			if err := c.BodyParser(&m); err != nil {
				// 若非json结构，尝试url.ParseQuery解析
				params, err := url.ParseQuery(string(c.Body()))
				if err == nil {
					for k, v := range params {
						if len(v) == 1 {
							m[k] = v[0]
						}
					}
				} else {
					log.Println("操作日志参数解析异常:" + err.Error())
				}
			}
		}

		if s, e := json.Marshal(m); e == nil {
			param = string(s)
		}

		// 构建日志信息
		var operLog pojo.SysOperlogVo
		operLog.Title = title
		operLog.BusinessType = int(businessType)
		operLog.Method = c.Path()
		operLog.RequestMethod = method

		// 操作者
		user := resp.GetUser(c)
		operLog.Device = user.Device
		operLog.OperName = user.Account

		operLog.OperUrl = c.Path()
		ip := ipaddr.New().GetIP(c)
		operLog.OperIp = ip
		operLog.OperLocation = ipaddr.New().GetRealAddressByIP(ip)
		// 请求参数
		operLog.OperParam = param

		// 执行下一个中间件或路由处理函数
		err := c.Next()
		operLog.CostTime = time.Since(start).Milliseconds()
		if err != nil {
			operLog.ErrorMsg = err.Error()
		} else {
			operLog.ErrorMsg = "-"
		}
		operLog.Status = c.Response().StatusCode()
		slog.Info("操作日志", slog.Any("data", operLog), slog.String("logType", "operlog"))
		return err
	}
}
